package inteligenciaartificial;

import java.util.Arrays;
import java.util.Collections;



public class Cruzador {
        
        public static int CantidadPuntosDeCruce = 2;
        public static double ProbabilidadCruzaBinomial= 0.5; 
        
	public static Individuo[] cruzaSimple(Individuo[] individuos, int cantidad){
		//checks
		if (cantidad==0) return null;
		if(individuos==null || individuos.length==0) return null;
		
		Individuo[] individuosCruzados= new Individuo[cantidad];
		
		Individuo[] mejoresIndividuos = individuos.clone();
		Individuo[] peoresIndividuos = individuos.clone();

                Arrays.sort(mejoresIndividuos,Collections.reverseOrder());
		Arrays.sort(peoresIndividuos);

                Individuo padre=null;
                Individuo madre=null;
                Individuo individuoCruzado=null;
                
                for (int i=0; i<cantidad; i++){
                    do{
                        if(i<cantidad/2){
                                padre= mejoresIndividuos[i];
                                madre= peoresIndividuos[i];
				individuoCruzado = Cruzador.cruzaSimple(padre,madre);
			}else if (i>=cantidad/2){
                                padre= peoresIndividuos[individuos.length-1 - i];
                                madre= mejoresIndividuos[individuos.length-1- i];
				individuoCruzado = Cruzador.cruzaSimple(padre,madre);;
			}
		
                    }while(!IndividuoManager.individuoCumpleRestricciones(individuoCruzado));
                    
                individuoCruzado.setNroGeneracionPadre(padre.getNroGeneracion());
                individuoCruzado.setNroIndividuoPadre(padre.getNroIndividuo());
                individuoCruzado.setNroGeneracionMadre(madre.getNroGeneracion());
                individuoCruzado.setNroIndividuoMadre(madre.getNroIndividuo());
                individuoCruzado.setOperadorGenerador(Individuo.Operador.CruzaSimple);
                individuosCruzados[i]= individuoCruzado;
                }
		return individuosCruzados;
	}
	
	private static Individuo cruzaSimple(Individuo padre, Individuo madre){
		if(padre==null || madre == null) return null;
		
		int[] genesHijo = new int[Individuo.CantidadDeGenes];
		
		int puntoDeCruce = (int) (Math.random() * Individuo.CantidadDeGenes);
		
		for (int i=0; i<Individuo.CantidadDeGenes; i++){
			if(i<=puntoDeCruce) genesHijo[i]= padre.getGen(i);
			else genesHijo[i]= madre.getGen(i);
		}
                
		return new Individuo(genesHijo);
	}
        
        public static Individuo[] cruzaMultipunto(Individuo[] individuos, int cantidad){
                if (cantidad==0) return null;
		if(individuos==null || individuos.length==0) return null;
		
		
		Individuo[] individuosCruzados= new Individuo[cantidad];
		
                Individuo[] mejoresIndividuos = individuos.clone();
		Individuo[] peoresIndividuos = individuos.clone();
                Arrays.sort(mejoresIndividuos,Collections.reverseOrder());
		Arrays.sort(peoresIndividuos);
                
                Individuo padre=null;
                Individuo madre=null;
                Individuo individuoCruzado=null;
           
                for (int i=0;i<cantidad;i++){                  
                      do{
                        if(i<cantidad/2){
                                padre= mejoresIndividuos[i];
                                madre= peoresIndividuos[i];
				individuoCruzado = Cruzador.cruzaMultipunto(padre,madre);
			}else if (i>=cantidad/2){
                                padre= peoresIndividuos[individuos.length-1 - i];
                                madre= mejoresIndividuos[individuos.length-1- i];
				individuoCruzado = Cruzador.cruzaMultipunto(padre,madre);;
			}
		
                    }while(!IndividuoManager.individuoCumpleRestricciones(individuoCruzado));
                    
                    individuoCruzado.setNroGeneracionPadre(padre.getNroGeneracion());
                    individuoCruzado.setNroIndividuoPadre(padre.getNroIndividuo());
                    individuoCruzado.setNroGeneracionMadre(madre.getNroGeneracion());
                    individuoCruzado.setNroIndividuoMadre(madre.getNroIndividuo());
                    individuoCruzado.setOperadorGenerador(Individuo.Operador.CruzaMultipunto);
                    individuosCruzados[i]= individuoCruzado;
                    
                }
		return individuosCruzados;
        }
        
        private static Individuo cruzaMultipunto(Individuo padre, Individuo madre){
            if(padre==null || madre == null) return null;
            
            int[] genesHijo = new int[Individuo.CantidadDeGenes];
            
            int[] puntoDeCruce= new int[Cruzador.CantidadPuntosDeCruce+2];
            //el primero y el ultimo son siempre cero y la cantidad de genes
            puntoDeCruce[0]=0;
            puntoDeCruce[puntoDeCruce.length-1]= Individuo.CantidadDeGenes;
            
            //genero puntos de cruce aleatoriamente
            for (int i=1; i<puntoDeCruce.length-1; i++){
                puntoDeCruce[i]= (int) (Math.random() * Individuo.CantidadDeGenes); 
            }
            //ordeno los puntos de cruce
            Arrays.sort(puntoDeCruce);

            //recorro todos los genes
            for (int i=0; i<Individuo.CantidadDeGenes; i++){  
                //recorro los valores de los puntos de cruce
                for(int j=0;j<puntoDeCruce.length-1;j++){
                    //si el gen numero i se encuentra en el ango dado por los puntos de cruce j y j+1 entro al condicional, 
                    //si es el ultimo valor tambien porque no entro en ninguno de los anteriores y tengo que asegurarme que se copie el gen
                    if((i>=puntoDeCruce[j] && i<puntoDeCruce[j+1]) || (j+1 ==puntoDeCruce.length) ) {
                        //los intervalos pares se copian del padre y los impares de la madre, ej: 0, padre, 1 madre, etc
                        if(j%2==0){
                            //del padre
                            genesHijo[i]= padre.getGen(i);
                        }else{
                            //de la madre
                             genesHijo[i]= madre.getGen(i);
                        }
                    }  
                }
            }
            return new Individuo(genesHijo);
        }
        
        public static Individuo[] cruzaBinomial(Individuo[] individuos, int cantidad){
                if (cantidad==0) return null;
		if(individuos==null || individuos.length==0) return null;
		
		Individuo[] individuosCruzados= new Individuo[cantidad];
		
                Individuo[] mejoresIndividuos = individuos.clone();
		Individuo[] peoresIndividuos = individuos.clone();
                Arrays.sort(mejoresIndividuos,Collections.reverseOrder());
		Arrays.sort(peoresIndividuos);
                
                Individuo padre=null;
                Individuo madre=null;
                Individuo individuoCruzado=null;
                
                
                for (int i=0;i<cantidad;i++){
                    
                    do{
                        if(i<cantidad/2){
                                padre= mejoresIndividuos[i];
                                madre= peoresIndividuos[i];
				individuoCruzado = Cruzador.cruzaMultipunto(padre,madre);
			}else if (i>=cantidad/2){
                                padre= peoresIndividuos[individuos.length-1 - i];
                                madre= mejoresIndividuos[individuos.length-1- i];
				individuoCruzado = Cruzador.cruzaBinomial(padre,madre);;
			}
		
                    }while(!IndividuoManager.individuoCumpleRestricciones(individuoCruzado));
                    
                individuoCruzado.setNroGeneracionPadre(padre.getNroGeneracion());
                individuoCruzado.setNroIndividuoPadre(padre.getNroIndividuo());
                individuoCruzado.setNroGeneracionMadre(madre.getNroGeneracion());
                individuoCruzado.setNroIndividuoMadre(madre.getNroIndividuo());
                individuoCruzado.setOperadorGenerador(Individuo.Operador.CruzaBinomial);
                individuosCruzados[i]= individuoCruzado;
                }
                return individuosCruzados;
        }
        
        private static Individuo cruzaBinomial(Individuo padre, Individuo madre){
            
                if(padre==null || madre == null) return null;
		int[] genesHijo = new int[Individuo.CantidadDeGenes];
                
                for (int i=0;i<genesHijo.length;i++){
                    if (Math.random()<= ProbabilidadCruzaBinomial){
                        genesHijo[i] = padre.getGen(i);
                    }
                    else{
                        genesHijo[i]=madre.getGen(i);
                    }
                    
                }
                return new Individuo(genesHijo);
        }
        
}
